/**
 * $Id: mxElbowEdgeHandler.java,v 1.11 2010-08-03 09:24:46 david Exp $
 * Copyright (c) 2008, Gaudenz Alder
 */
package com.mxgraph.swing.handler;

import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxResources;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraphView;
import com.mxgraph.view.mxConnectionConstraint;
import com.mxgraph.view.mxGraph;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;

/**
 * Created by IntelliJ IDEA.
 *
 * @author Wang Jiaxiang
 *         Date: 2011-2-21
 */
public class htEdgeHandler extends mxEdgeHandler {
    private static final int LINE_HORIZONTAL= 1;
    private static final int LINE_VETRIC= 2;

    
    /**
     * @param graphComponent
     * @param state
     */
    public htEdgeHandler(mxGraphComponent graphComponent, mxCellState state) {
        super(graphComponent, state);
    }


    /**
     *
     */
    protected boolean isFlipEvent(MouseEvent e) {
        return e.getClickCount() == 2 && index == 1;
    }

    /**
     * Returns true if the given index is the index of the last handle.
     */
    public boolean isLabel(int index) {
        return false;
    }

    protected boolean isHandleVisible(int index) {
        if(isSource(index) || isTarget(index) || index % 2 == 1)
          return true;
        return false;
    }

    public void mouseDragged(MouseEvent e) {
        if (!e.isConsumed() && first != null) {
            gridEnabledEvent = graphComponent.isGridEnabledEvent(e);
            constrainedEvent = graphComponent.isConstrainedEvent(e);
            boolean isSource = isSource(index);
            boolean isTarget = isTarget(index);

            Object source = null;
            Object target = null;
            mxCellState clone = (mxCellState) state.clone();
            mxGeometry geometry = graphComponent.getGraph()
                        .getCellGeometry(state.getCell());
            if (isLabel(index)) {
                mxPoint abs = state.getAbsoluteOffset();
                double dx = abs.getX() - first.x;
                double dy = abs.getY() - first.y;

                mxPoint pt = new mxPoint(e.getPoint());

                if (gridEnabledEvent) {
                    pt = graphComponent.snapScaledPoint(pt, dx, dy);
                }

                if (constrainedEvent) {
                    if (Math.abs(e.getX() - first.x) > Math.abs(e.getY()
                            - first.y)) {
                        pt.setY(abs.getY());
                    } else {
                        pt.setX(abs.getX());
                    }
                }

                Rectangle rect = getPreviewBounds();
                rect.translate((int) Math.round(pt.getX() - first.x),
                        (int) Math.round(pt.getY() - first.y));
                preview.setBounds(rect);
            } else {
                // Clones the cell state and updates the absolute points using
                // the current state of this handle. This is required for
                // computing the correct perimeter points and edge style.
                List<mxPoint> abslutPoints = clone.getAbsolutePoints();
                List<mxPoint> points = geometry.getPoints();
                mxGraphView view = clone.getView();

                if (isSource || isTarget) {
                    return;
                    //marker.process(e);
                    //mxCellState currentState = marker.getValidState();
                    //
                    //target = view
                    //        .getVisibleTerminal(state.getCell(), !isSource);
                    //
                    //if (currentState != null) {
                    //    source = currentState.getCell();
                    //} else {
                    //    mxPoint pt = new mxPoint(e.getPoint());
                    //
                    //    if (gridEnabledEvent) {
                    //        pt = graphComponent.snapScaledPoint(pt);
                    //    }
                    //
                    //    clone.setAbsoluteTerminalPoint(pt, isSource);
                    //}
                    //
                    //if (!isSource) {
                    //    Object tmp = source;
                    //    source = target;
                    //    target = tmp;
                    //}
                } else {
                    mxPoint point = convertPoint(new mxPoint(e.getPoint()),
                            gridEnabledEvent);
                        //to change two terminals of the line who need tobe moved
                        int lineDirect;
                        mxPoint prePoint = convertPoint(abslutPoints.get(index - 1), gridEnabledEvent);
                        mxPoint nextPoint = convertPoint(abslutPoints.get(index + 1), gridEnabledEvent);
                        int prePointIdx = index/2;
                        int nextPointIdx = index/2 + 1;
                        if(prePoint.getX() == nextPoint.getX())
                            lineDirect = LINE_HORIZONTAL;
                        else if(prePoint.getY() == nextPoint.getY())
                            lineDirect = LINE_VETRIC;
                        else if(Math.abs(prePoint.getX() - nextPoint.getX()) < 5.0)
                            lineDirect = LINE_HORIZONTAL;
                        else
                            lineDirect = LINE_VETRIC;
                        switch(lineDirect) {
                            case LINE_HORIZONTAL :
                                prePoint.setX(point.getX());
                                nextPoint.setX(point.getX());
                                break;
                            case LINE_VETRIC:
                                prePoint.setY(point.getY());
                                nextPoint.setY(point.getY());
                                break;
                        }
                        points.set(prePointIdx, prePoint);
                        points.set(nextPointIdx, nextPoint);

                    source = view.getVisibleTerminal(state.getCell(), true);
                    target = view.getVisibleTerminal(state.getCell(), false);
                }

                // Computes the points for the edge style and terminals
                mxCellState sourceState = view.getState(source);
                mxCellState targetState = view.getState(target);

                /* TODO: Implement mxConstraintHandler
                    mxConnectionConstraint constraint = constraintHandler.currentConstraint;

                    if (constraint == null)
                    {
                        constraint = new mxConnectionConstraint();
                    }

                    if (isSource)
                    {
                        sourceConstraint = constraint;
                    }
                    else if (isTarget)
                    {
                        targetConstraint = constraint;
                    }
                    */
                geometry.setSourcePoint(clone.getAbsolutePoints().get(0));
                geometry.setTargetPoint(clone.getAbsolutePoints().get(clone.getAbsolutePoints().size() -1));
                view.updatePoints(clone, points, sourceState, targetState);
                p = createPoints(clone);
                preview.setBounds(getPreviewBounds());
            }
            state.setAbsolutePoints(clone.getAbsolutePoints());
            //geometry.setSourcePoint(clone.getAbsolutePoints().get(0));
            //geometry.setTargetPoint(clone.getAbsolutePoints().get(clone.getAbsolutePoints().size() -1));
            if (!preview.isVisible()
                    && graphComponent.isSignificant(e.getX() - first.x, e
                    .getY()
                    - first.y)) {
                preview.setVisible(true);

            } else if (preview.isVisible()) {
                preview.repaint();
            }
            e.consume();
        }

    }

    public void mouseReleased(MouseEvent e) {
        if(isTarget(index) || isSource(index))
            return;
       mxGraph graph = graphComponent.getGraph();
        if (!e.isConsumed() && first != null) {
            double dx = e.getX() - first.x;
            double dy = e.getY() - first.y;
            if (graphComponent.isSignificant(dx, dy)) {
                if (error != null) {
                    if (error.length() > 0) {
                        JOptionPane.showMessageDialog(graphComponent, error);
                    }
                } else if (isLabel(index)) {
                    mxPoint abs = state.getAbsoluteOffset();
                    dx = abs.getX() - first.x;
                    dy = abs.getY() - first.y;

                    mxPoint pt = new mxPoint(e.getPoint());

                    if (gridEnabledEvent) {
                        pt = graphComponent.snapScaledPoint(pt, dx, dy);
                    }

                    if (constrainedEvent) {
                        if (Math.abs(e.getX() - first.x) > Math.abs(e.getY()
                                - first.y)) {
                            pt.setY(abs.getY());
                        } else {
                            pt.setX(abs.getX());
                        }
                    }
                    moveLabelTo(state, pt.getX() + dx, pt.getY() + dy);
                } else if (marker.hasValidState()
                        && (isSource(index) || isTarget(index))) {
                    connect(state.getCell(), marker.getValidState().getCell(),
                            isSource(index), graphComponent.isCloneEvent(e)
                            && isCloneEnabled());
                } else if ((!isSource(index) && !isTarget(index))
                        || graphComponent.getGraph().isAllowDanglingEdges()) {
                    moveControlPoint(state.getCell(), index, convertPoint(new mxPoint(
                            e.getPoint()), gridEnabledEvent));
                }
                e.consume();
            }
        }

        if (!e.isConsumed() && isFlipEvent(e)) {
            graph.flipEdge(state.getCell());
            e.consume();
        }
        graphComponent.refresh();
    }

    private void moveControlPoint(Object edge, int pointIndex, mxPoint point) {

        mxIGraphModel model = graphComponent.getGraph().getModel();
        mxGeometry geometry = model.getGeometry(edge);
        if (geometry != null) {
            model.beginUpdate();
            try {
                geometry = (mxGeometry) geometry.clone();

               if (isSource(index) || isTarget(index)) {
                    connect(edge, null, isSource(index), false);
                    geometry.setTerminalPoint(point, isSource(index));
                } else {
                    List<mxPoint> pts = geometry.getPoints();
                    if (pts == null) {
                        pts = new ArrayList<mxPoint>();
                        geometry.setPoints(pts);
                    }
                }
                model.setGeometry(edge, geometry);
            }
            finally {
                model.endUpdate();
            }
        }
    }
}